Introduce move expressions (move($expr)) #155023
Introduce move expressions (move($expr)) #155023TaKO8Ki wants to merge 15 commits intorust-lang:mainfrom
move($expr)) #155023Conversation
move($expr)) move($expr))
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Some changes occurred in src/tools/clippy cc @rust-lang/clippy The parser was modified, potentially altering the grammar of (stable) Rust cc @fmease Some changes occurred in src/tools/rustfmt cc @rust-lang/rustfmt |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
replace TODO with FIXME
document move expression lowering flow
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
|
||
| match closure.coroutine_kind { | ||
| // FIXME(TaKO8Ki): Support `move(expr)` in coroutine closures too. | ||
| // For the first step, we only support plain closures. |
There was a problem hiding this comment.
Hmm. I think it's fine to leave this for follow-up, but also I expect it will be quite straightforward to do.
| // selects the synthetic local for this exact `move(...)` occurrence. | ||
| if let Some((ident, binding)) = self | ||
| .move_expr_bindings | ||
| .last() |
There was a problem hiding this comment.
do we push a new entry for every closure that we are lowering?
| // During body lowering, replace each `move(...)` occurrence with the | ||
| // synthetic local recorded in this closure's binding map. Nested closures | ||
| // push their own maps. | ||
| self.move_expr_bindings.push(bindings); |
There was a problem hiding this comment.
Something about this feels "off" to me. I think this push should be part of lower_expr_closure rather than here. For one thing, I don't think that these move_expr_bindings should be in scope when we call lower_expr on line 1210, but for another, the call to move_expr_bindings.last()` implies to me that we always push something on the vector. I know that we always push things on the vector when it matters, but it still feels "odd".
Here is an interesting test case:
let v = "Hello, Ferris".to_string();
let r = || {
|| {
move(move(v.clone())).len()
}
};
println!("{v}, {}", r());I don't exactly know what this will do. It depends on whether we visit the contents of move expressions in that collection visitor. I think what I would like it to do is to follow the desgaring. But for sure, with the code as it is right now, when we walk the outer closure "up-front" and we will fail to detect the move because it is contained in a nested closure.
I wonder about doing this a different way. What if we:
- when we enter a closure body, we push an empty vector for "move expressions". When we enter other bodies (e.g., a top-level function), we push a placeholder like
None. - when we encounter a
moveexpression, we check if that vector isSomeorNone.Noneresults in an error ("move" outside of closure). ForSome, we push the expression (unlowered, I think?) into the vector and then we create a new variable and return it.
When we finish processing a closure (i.e., here) we pop that vector and walk over all the things that were collected, and lower the expressions. This should naturally "unfold" in the recursive case the way I expect.
Would this work? Or is there some complexity about the ordering when variables are created or something?
(Alternatively, we always push a vector, not an option, but then we error if, after a non-closure body, the vector is non-empty, but that seems a bit less robust to me (we have to remember to check everywhere).)
There was a problem hiding this comment.
what I want to happen:
let v = "Hello, Ferris".to_string();
let r = || {
|| {
move(move(v.clone())).len()
}
};
println!("{v}, {}", r());becomes
let v = "Hello, Ferris".to_string();
let r = || {
|| {
move(move(v.clone())).len()
}
};
println!("{v}, {}", r());becomes
let v = "Hello, Ferris".to_string();
let tmp0 = v.clone();
let r = move(tmp0) || {
let tmp1 = tmp0;
move(tmp1) || {
tmp1.len()
}
};
println!("{v}, {}", r());| allow_async_fn_traits: Arc<[Symbol]>, | ||
|
|
||
| delayed_lints: Vec<DelayedLint>, | ||
| move_expr_bindings: Vec<NodeMap<(Ident, HirId)>>, |
There was a problem hiding this comment.
under my proposed change, this would be Vec<Option<NodeMap<_>>>
View all comments
This is an experimental first version of move expressions.
This first version implements it just in plain closures. A support for coroutine closures will be added in follow up pull requests.
RFC: will be added later
Tracking issue: #155050
Project goal:
r? @nikomatsakis